package Model;

import Agents.Corpse;
import Agents.Human;
import Agents.Virus;
import Agents.Zombie;
import Helpers.EnumBehaviour;
import Param.Constants;
import sim.engine.SimState;
import sim.engine.Stoppable;
import sim.field.grid.SparseGrid2D;
import sim.util.Int2D;

@SuppressWarnings("serial")
public class SimModel extends SimState {
	public SparseGrid2D yard;
	private int GRID_SIZE;

	//Initialization
	private int numHumanGroup;
	private int numHumanSeul;
	private int numHumanFuir;

	private int numCorpse;
	private int numZombie;

	private int humanExpectancy;
	
	private boolean init = false;

	//Dynamic

	public SimModel(long seed) {
		super(seed);

		initParameters();

		yard = new SparseGrid2D (GRID_SIZE, GRID_SIZE);
	}

	public void start() {
		super.start();
		yard.clear();

		// Agents
		initHumans();
		initZombies();
		
		this.init = true;
	}
	

	@Override
	public void finish() {
		this.init = false;
		super.finish();
		initParameters();
	}

	public void initParameters() {
		GRID_SIZE = Constants.GRID_SIZE;

		//TODO verif compteurs entites
		numHumanGroup = Constants.INIT_NUM_HUMAN_GROUP;
		numHumanSeul = Constants.INIT_NUM_HUMAN_SEUL;
		numHumanFuir = Constants.INIT_NUM_HUMAN_FUIR;
		numZombie = Constants.INIT_NUM_ZOMBIE;

		humanExpectancy = Constants.HUMAN_EXPECTANCY;

		numCorpse = 0;
	}
	
	/** Create Entites **/

	private void initHumans() {
		for (int i = 0; i < numHumanGroup; i++) {
			addHuman(EnumBehaviour.GATHER);
		}
		for (int i = 0; i < numHumanSeul; i++) {
			addHuman(EnumBehaviour.ALONE);
		}
		for (int i = 0; i < numHumanFuir; i++) {
			addHuman(EnumBehaviour.FLEE);
		}
	}
	private void addHuman(EnumBehaviour b) {
		//Init position
		Int2D location = null;
		do{
			location = new Int2D(random.nextInt(yard.getWidth()),random.nextInt(yard.getHeight()) );
		}
		while (yard.getObjectsAtLocation(location.x,location.y) != null);

		Human h = new Human(this, location, b);
		yard.setObjectLocation(h, location.x,location.y);
		Stoppable s = schedule.scheduleRepeating(h);
		h.setStoppable(s);

		if(init){
			switch(h.getBehaviour()){
			case GATHER :
				numHumanGroup++;
				break;
	
			case FLEE :
				numHumanFuir++;
				break;
	
			case ALONE :
				numHumanSeul++;
				break;
			}
		}
	}


	public void addCorpse(Int2D location, Virus virus){
		Corpse c = new Corpse(this, location, virus);
		yard.setObjectLocation(c, location.x,location.y);
		Stoppable s = schedule.scheduleRepeating(c);
		c.setStoppable(s);
		
		if(init){
			numCorpse++;
		}
	}


	private void initZombies() {
		for (int i = 0; i < numZombie; i++) {
			addZombie();
		}
	}
	public void addZombie(){
		//Init position
		Int2D location = null;
		do{
			location = new Int2D(random.nextInt(yard.getWidth()),random.nextInt(yard.getHeight()) );
		}
		while (yard.getObjectsAtLocation(location.x,location.y) != null);

		addZombie(location);
	}
	public void addZombie(Int2D location){
		Zombie z = new Zombie(this, location);
		yard.setObjectLocation(z, location.x,location.y);
		Stoppable s = schedule.scheduleRepeating(z);
		z.setStoppable(s);

		if(init){
			numZombie++;
		}
	}


	/** Delete Entites **/

	public void deleteHuman(Human h){
		switch(h.getBehaviour()){
		case GATHER :
			numHumanGroup--;
			break;

		case FLEE :
			numHumanFuir--;
			break;

		case ALONE :
			numHumanSeul--;
			break;
		}
	}
	public void deleteCorpse(){
		numCorpse--;
	}
	public void deleteZombie(){
		numZombie--;
	}


	/** Yard helpers **/

	public boolean isFree(Int2D l){
		return isFree(l.x, l.y);
	}
	public boolean isFree(int x, int y){
		return yard.getObjectsAtLocation(x, y) == null;
	}

	public boolean inBounds(Int2D l) {
		return inBounds(l.x, l.y);
	}
	public boolean inBounds(int x, int y) {
		return (x >= 0 && x < yard.getWidth() && y >= 0 && y < yard.getHeight());
	}


	/** Getters / Setters **/

	public int getNumHuman(){
		return getNumHumanGroup() + getNumHumanSeul() + getNumHumanFuir();
	}

	public int getNumHumanGroup() {
		return numHumanGroup;
	}

	public int getNumHumanSeul() {
		return numHumanSeul;
	}

	public int getNumHumanFuir() {
		return numHumanFuir;
	}
	
	public int getHumanExpectancy() {
		return humanExpectancy;
	}

	public int getNumCorpse() {
		return numCorpse;
	}

	public int getNumZombie() {
		return numZombie;
	}

	public void setNumHumanGroup(int numHumanGroup) {
		this.numHumanGroup = numHumanGroup;
	}

	public void setNumHumanSeul(int numHumanSeul) {
		this.numHumanSeul = numHumanSeul;
	}

	public void setNumHumanFuir(int numHumanFuir) {
		this.numHumanFuir = numHumanFuir;
	}

	public void setNumZombie(int numZombie) {
		this.numZombie = numZombie;
	}
	
}
